package com.sirun.ble.services;


import static com.sirun.ble.constants.BleConfig.BleSdkTag;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_DEFAULT;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_FIND_CAR;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_LOCK;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_UNLOCK;
import static com.sirun.ble.constants.BleConfig.CONTROL_STEP_CMD_SEND;
import static com.sirun.ble.constants.BleConfig.CONTROL_STEP_FAIL;
import static com.sirun.ble.constants.BleConfig.CONTROL_STEP_SUCCESS;
import static com.sirun.ble.constants.BleConfig.CONTROL_STEP_UNDER_CONTROL;
import static com.sirun.ble.constants.BleConfig.CURRENT_CMD_TYPE;
import static com.sirun.ble.constants.BleConfig.TAG_CONNECTED;
import static com.sirun.ble.constants.BleConfig.TAG_CONNECTED_AUTH;
import static com.sirun.ble.constants.BleConfig.TAG_CONNECTED_NONE;

import android.annotation.SuppressLint;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.SparseArray;

import com.sirun.ble.IMyAidlInterface;
import com.sirun.ble.bean.CarStatusBean;
import com.sirun.ble.enums.BleEnum;
import com.sirun.ble.listeners.OnBleVehicleStatusListener;
import com.sirun.ble.listeners.OnBleConnectListener;
import com.sirun.ble.listeners.OnBleControlListener;
import com.sirun.ble.listeners.OnBleDownloadListener;
import com.sirun.ble.listeners.OnBleScanListener;
import com.sirun.ble.listeners.OnBleSpareListener;
import com.sirun.ble.listeners.OnCmdListener;
import com.sirun.ble.utils.AwByteUtil;
import com.sirun.ble.utils.AwDataUtil;
import com.sirun.ble.utils.AwLog;
import com.sirun.ble.utils.sdk.AppBleCmdUtil;
import com.sirun.ble.utils.sdk.AwBleCallbackUtil;
import com.sirun.ble.utils.sdk.AwBleRssiUtil;
import com.sirun.ble.utils.sdk.AwBleUtil;
import com.sirun.ble.utils.sdk.AwBleSpUtil;
import com.sirun.ble.utils.sec.AwAesUtil;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @author : hzw
 */
@SuppressLint("MissingPermission")
public class BleService extends Service {

    private static final String TAG = "SERVICE";
    private static final int DEFALUT_COUNTDOWN_TIME = 20000;
    private UUID UUID_SERVICE = null; //Common Service
    private UUID UUID_SERVICE_OTA = null; //OTA Service 0x2600
    private UUID UUID_OTA = null; //OTA Characteristics  0x7001
    private UUID UUID_AUTH = null; //鉴权Characteristics 0x7800
    private UUID UUID_CONTROL = null; //控制Characteristics 0x7801
    private UUID UUID_QUERY = null; //查询Characteristics 0x7802
    private UUID UUID_REPORT = null; //上报Characteristics 0x7803
    private UUID UUID_PAIR = null; //配对Characteristics 0x7804
    private UUID UUID_HEART = null; //心跳Characteristics 共用 7802
    private UUID UUID_CHAR_READ_NOTIFY = null;
    private UUID UUID_DESC_NOTITY = null;

    private Context mContext = BleService.this;
    public boolean isScanning = false;
    public boolean isConnected = false;
    private BluetoothGatt mBluetoothGatt;
    private Handler mHandler = new Handler();
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothLeScanner mBluetoothLeScanner;

    private OnBleConnectListener mBleConnectListener;
    private OnBleScanListener mBleScanListener;
    private OnBleDownloadListener mBleDownloadListener;
    private OnBleSpareListener mBleSpareListener;
    private OnBleControlListener mBleControlListener;
    private OnBleVehicleStatusListener mBleVehicleStatusListener;

    private BluetoothDevice mBluetoothDevice;
    private MyCountTime mCountTime;
    private final ScanCallback mBleScanCallback = new MyScanCallback();
    private boolean isAuth = false;
    private boolean isRetry = false;
    private boolean isDiscovery = false;
    private boolean isNotifySuccess = false;
    private int authCount = 0;
    private HeartRunnable mHeartRunnable = new HeartRunnable();
    private boolean isDisConnecting = false;
    public BluetoothGattCallback mBluetoothGattCallback = new MyBluetoothGattCallback();

    public class MyBinder extends IMyAidlInterface.Stub {

        @Override
        public String getName() throws RemoteException {
            return BleService.class.getName();
        }
    }

    public MyBinder mBinder = new MyBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        CURRENT_CMD_TYPE = CMD_TYPE_DEFAULT;
        initBleAdapterAndScanner();
//        setCmdCallback(BleByteUtil.getHexBytes("5510bae1e441fd5866838495c16417ea4086aa5510ef6c979a6aab3b4fa36b6b502a2a5a10aa"));
    }

    private void initBleAdapterAndScanner() {
        if(mBluetoothAdapter == null) {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        if(mBluetoothLeScanner == null) {
            mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        }
    }

    public boolean isAuthConnected() {
        return isConnected && isAuth;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        closeConnect(false);
        if(mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
    }

    public void setConnectListener(OnBleConnectListener bleConnectListener) {
        mBleConnectListener = bleConnectListener;
        if(mBleConnectListener == null) {
            AwLog.d(TAG,"setConnectListener", "bleservice set connectlistener is null");
        } else {
            AwLog.d(TAG, "setConnectListener", "bleservice set connectlistener != null");
        }
    }

    public void setScanListener(OnBleScanListener bleScanListener) {
        mBleScanListener = bleScanListener;
    }

    public void setDownloadListener(OnBleDownloadListener bleDownloadListener) {
        mBleDownloadListener = bleDownloadListener;
    }

    public void setSpareListener(OnBleSpareListener bleSpareListener) {
        mBleSpareListener = bleSpareListener;
    }

    public void setControlListener(OnBleControlListener bleControlListener) {
        mBleControlListener = bleControlListener;
    }

    public void setCarStatusListener(OnBleVehicleStatusListener bleVehicleStatusListener) {
        mBleVehicleStatusListener = bleVehicleStatusListener;
    }

    public void startScanBle() {
//        loopCarStatusTest();
        if(AwBleUtil.checkPariedBle(mContext) != null) {
            AwLog.d(TAG, "startScanBle", "startScanBle pre check. Paired, connect directly.");
            mBluetoothDevice = AwBleUtil.checkPariedBle(mContext);
            toConnect();
            return;
        }
        AwLog.d(TAG, "startScanBle", "startScanBle pre check. not Paired, to scan.");
        if(mBluetoothLeScanner == null) {
            AwLog.d(TAG, "startScanBle", "startScanBle bluetoothLeScanner is null, delay and retry");
            mHandler.postDelayed(new ScanRunnable(), 2000);
            return;
        }
        if(isDisConnecting) {
            AwLog.d(TAG, "startScanBle", "startScanBle, However, isDisConnecting true, , delay and retry");
            mHandler.postDelayed(new ScanRunnable2(), 1000);
            return;
        }
        AwLog.d(TAG, "startScanBle", "pre checked passed...");
        mHandler.postDelayed(new ScanRunnable3(), AwBleUtil.getIntervalTime(mContext));
    }

    public class ScanRunnable implements Runnable {
        @Override
        public void run() {
            initBleAdapterAndScanner();
            startScanBle();
            AwLog.d(TAG, "ScanRunnable", "...");
        }
    }

    public class ScanRunnable2 implements Runnable {
        @Override
        public void run() {
            startScanBle();
            AwLog.d(TAG, "ScanRunnable2", "...");
        }
    }

    public class ScanRunnable3 implements Runnable {
        @Override
        public void run() {
            startScanCountDown();
            AwLog.d(TAG, "ScanRunnable3", "...");
        }
    }


    private void startScanCountDown() {
        if(isScanning) {
            AwLog.d(TAG, "startScanCountDown", "isScanning. cancel and retry startScanCountDown, return.");
            cancelScanCountdown();
            mHandler.postDelayed(new ScanRunnable3(), 2000);
            return;
        }
        AwLog.d(TAG, "startScanCountDown", "pre check passed...");
        if(mCountTime == null) {
            mCountTime = new MyCountTime(DEFALUT_COUNTDOWN_TIME, 1000);
        }
        isScanning = true;
        mCountTime.start();
        mBluetoothLeScanner.startScan(mBleScanCallback);
    }

    private void cancelScanCountdown() {
        AwLog.d(TAG, "cancelScanCountdown", "...");
        if(mCountTime != null) {
            mCountTime.cancel();
            mCountTime = null;
            isScanning = false;
        }
        stopScanBle();//停止扫描
    }

    private void startHeatBeatTask() {
        AwLog.d(TAG, "startHeatBeatTask", "...");
        sendHeartBeat();
    }

    private void cancelHeatBeatTask() {
        AwLog.d(TAG, "cancelHeatBeatTask", "...");
        if (mHandler != null) {
            mHandler.removeCallbacks(mHeartRunnable);
        } else {
            AwLog.d(TAG, "cancelHeatBeatTask", "Handler is null");
        }
    }

    public void reScanBle() {
        AwLog.d(TAG, "reScanBle", "...");
        startScanBle();
    }

    public void stopScanBle() {
        if(mBluetoothLeScanner == null) {
            AwLog.d(TAG, "stopScanBle", "mBluetoothLeScanner is null");
            return;
        }
        AwLog.d(TAG, "stopScanBle", "...");
        try {
            mBluetoothLeScanner.stopScan(mBleScanCallback);
        } catch (Exception e) {
            e.printStackTrace();
            AwLog.d(TAG, "stopScanBle", "exception, e: " + e.getMessage());
        }
    }

    public void connectBle(BluetoothDevice dev) {
        AwLog.d(TAG, "connectBle", "dev: " + dev.toString());
//        closeConnect(false);
        mBluetoothGatt = dev.connectGatt(mContext, false, mBluetoothGattCallback);
        mBluetoothGatt.connect();
    }

    // BLE中心设备连接外围设备的数量有限(大概2~7个)，在建立新连接之前必须释放旧连接资源，否则容易出现连接错误133
    public void closeConnect(boolean needCallback) {
        if(isDisConnecting) {
            AwLog.d(TAG, "closeConnect", "isDisConnecting true, return");
            return;
        }
        AwLog.d(TAG, "closeConnect", "...");
        try {
            cancelHeatBeatTask();
            cancelScanCountdown();
            if (mBluetoothGatt != null) {
                mBluetoothGatt.disconnect();
                mBluetoothGatt.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
            AwLog.d(TAG, "closeConnect", "Exception e: " + e.getMessage());
        }
        if(needCallback) {
            if(mBleConnectListener == null) {
                AwLog.d(TAG, "closeConnect", "BleConnectListener is null");
                return;
            }
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_CLOSE, TAG_CONNECTED_NONE);
        }
        isConnected = false;
        isAuth = false;
        isDisConnecting = true;
        isDiscovery = false;
        isNotifySuccess = false;
        mHandler.postDelayed(new CloseConnectRunnable(), 2000);
    }

    public class CloseConnectRunnable implements Runnable {

        @Override
        public void run() {
            AwLog.d(TAG, "CloseConnectRunnable", "...");
            AwBleSpUtil.setBleDisconnectTime(mContext, String.valueOf(System.currentTimeMillis()));
            isDisConnecting = false;
        }
    }

    // 获取Gatt服务
    private BluetoothGattService getGattService(UUID uuid) {
        if (!isConnected) {
            AwLog.d(TAG,  "getGattService", "no connect");
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NONE, TAG_CONNECTED_NONE);
            return null;
        }
        if(uuid == null) {
            AwLog.d(TAG, "getGattService","uid is null");
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
            return null;
        }
        BluetoothGattService service = mBluetoothGatt.getService(uuid);
        if (service == null) {
            closeConnect(true);
            AwLog.d(TAG, "getGattService","findServiceByid, service is null, UUID = " + uuid);
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
        }
        return service;
    }

    // 注意：连续频繁读写数据容易失败，读写操作间隔最好200ms以上，或等待上次回调完成后再进行下次读写操作！
    // 读取数据成功会回调->onCharacteristicChanged()
    private void read() {
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CHAR_READ_NOTIFY);//通过UUID获取可读的Characteristic
            mBluetoothGatt.readCharacteristic(characteristic);
        }
    }

    // 注意：连续频繁读写数据容易失败，读写操作间隔最好200ms以上，或等待上次回调完成后再进行下次读写操作！
    // 写入数据成功会回调->onCharacteristicWrite()
    private void write(Object txt) {
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_REPORT);//通过UUID获取可写的Characteristic
            if(txt instanceof byte[]) {
                characteristic.setValue((byte[]) txt); //单次最多20个字节
            } else if (txt instanceof String) {
                byte[] bytes = AwByteUtil.getHexBytes((String) txt);
                characteristic.setValue(bytes); //单次最多20个字节

            }
            mBluetoothGatt.writeCharacteristic(characteristic);
        }
    }

    // 设置通知Characteristic变化会回调->onCharacteristicChanged()
    private boolean setNotify() {
        AwLog.d(TAG, "setNotify","UUID_SERVICE: " + UUID_SERVICE);
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            //鉴权特征通知订阅
            BluetoothGattCharacteristic authCharacteristic = service.getCharacteristic(UUID_AUTH);
            if(authCharacteristic == null) {
                AwLog.d(TAG, "setNotify","authCharacteristic is null");
                return false;
            }
            if(mBluetoothGatt == null) {
                AwLog.d(TAG, "setNotify","mBluetoothGatt is null");
                return false;
            }
            isNotifySuccess = mBluetoothGatt.setCharacteristicNotification(authCharacteristic, true);
            AwLog.d(TAG, "setNotify","authCharacteristic result: " + isNotifySuccess);
            if(isNotifySuccess) {
                List<BluetoothGattDescriptor> tempList = authCharacteristic.getDescriptors();
                for(BluetoothGattDescriptor descriptor : tempList) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean writeResult = mBluetoothGatt.writeDescriptor(descriptor);
                    AwLog.d(TAG, "setNotify","authCharacteristic descriptor: " + descriptor.getUuid() + " ,writeResult: " + writeResult);
                }
            }
            //控制特征通知订阅
            BluetoothGattCharacteristic controlCharacteristic = service.getCharacteristic(UUID_CONTROL);
            isNotifySuccess = mBluetoothGatt.setCharacteristicNotification(controlCharacteristic, true);
            AwLog.d(TAG, "setNotify","controlCharacteristic result: " + isNotifySuccess);
            if(isNotifySuccess) {
                List<BluetoothGattDescriptor> tempList = controlCharacteristic.getDescriptors();
                for(BluetoothGattDescriptor descriptor : tempList) {
                    AwLog.d(TAG, "setNotify","controlCharacteristic descriptorUUID: " + descriptor.getUuid());
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean writeResult = mBluetoothGatt.writeDescriptor(descriptor);
                    AwLog.d(TAG, "setNotify","controlCharacteristic descriptor: " + descriptor.getUuid() + " ,writeResult: " + writeResult);
                }
            }
            //查询特征通知订阅
            BluetoothGattCharacteristic queryCharacteristic = service.getCharacteristic(UUID_QUERY);
            isNotifySuccess = mBluetoothGatt.setCharacteristicNotification(queryCharacteristic, true);
            AwLog.d(TAG, "setNotify","queryCharacteristic result: " + isNotifySuccess);
            if(isNotifySuccess) {
                List<BluetoothGattDescriptor> tempList = queryCharacteristic.getDescriptors();
                for(BluetoothGattDescriptor descriptor : tempList) {
                    AwLog.d(TAG, "setNotify","queryCharacteristic descriptorUUID: " + descriptor.getUuid());
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean writeResult = mBluetoothGatt.writeDescriptor(descriptor);
                    AwLog.d(TAG, "setNotify","queryCharacteristic descriptor: " + descriptor.getUuid() + " ,writeResult: " + writeResult);

                }
            }
            //上报特征通知订阅
            BluetoothGattCharacteristic reportCharacteristic = service.getCharacteristic(UUID_REPORT);
            isNotifySuccess = mBluetoothGatt.setCharacteristicNotification(reportCharacteristic, true);
            AwLog.d(TAG, "setNotify", "reportCharacteristic result: " + isNotifySuccess);
            if(isNotifySuccess) {
                List<BluetoothGattDescriptor> tempList = reportCharacteristic.getDescriptors();
                for(BluetoothGattDescriptor descriptor : tempList) {
                    AwLog.d(TAG, "setNotify", "reportCharacteristic descriptorUUID: " + descriptor.getUuid());
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean writeResult = mBluetoothGatt.writeDescriptor(descriptor);
                    AwLog.d(TAG, "setNotify", "reportCharacteristic descriptor: " + descriptor.getUuid() + " ,writeResult: " + writeResult);
                }
            }
            //配对特征通知订阅
            BluetoothGattCharacteristic pariCharacteristic = service.getCharacteristic(UUID_PAIR);
            isNotifySuccess = mBluetoothGatt.setCharacteristicNotification(pariCharacteristic, true);
            AwLog.d(TAG, "setNotify", "pariCharacteristic result: " + isNotifySuccess);
            if(isNotifySuccess) {
                List<BluetoothGattDescriptor> tempList = pariCharacteristic.getDescriptors();
                for(BluetoothGattDescriptor descriptor : tempList) {
                    AwLog.d(TAG, "setNotify", "pariCharacteristic descriptorUUID: " + descriptor.getUuid());
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean writeResult = mBluetoothGatt.writeDescriptor(descriptor);
                    AwLog.d(TAG, "setNotify", "pariCharacteristic descriptor: " + descriptor.getUuid() + " ,writeResult: " + writeResult);
                }
            }
            if(isNotifySuccess) {
                AwLog.d(TAG, "setNotify", "通知设置成功...监听蓝牙反馈信息中....");
            } else {
                AwLog.d(TAG, "setNotify", "通知设置失败");
                AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NOTIFY_FAIL, TAG_CONNECTED);
                closeConnect(true);
            }
            return isNotifySuccess;
        } else {
            AwLog.d(TAG, "setNotify", "BluetoothGattService is null");
            return false;
        }
    }

    private void authentication() {
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            AwLog.d(TAG, "authentication", "SecKey: " + AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(mContext)) + " ,index: " + AwAesUtil.deFile(AwBleSpUtil.getBleMacIndex(mContext)));
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_AUTH);
//            BleLog.dN(TAG, "send authentication...");
            characteristic.setValue(AppBleCmdUtil.getEncryptAuthCmd(mContext));
            mBluetoothGatt.writeCharacteristic(characteristic);
        } else {
            AwLog.d(TAG, "authentication", "BluetoothGattService is null");
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
        }
    }

    private void sendModelInfo() {
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            AwLog.d(TAG, "sendModelInfo", "SecKey: " + AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(mContext)) + " ,index: " + AwAesUtil.deFile(AwBleSpUtil.getBleMacIndex(mContext)));
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_QUERY);
            characteristic.setValue(AppBleCmdUtil.getModelInfoCmd(mContext));
            mBluetoothGatt.writeCharacteristic(characteristic);
        } else {
            AwLog.d(TAG, "sendModelInfo","BluetoothGattService is null");
        }
    }

    private void sendHeartBeat() {
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_HEART);
            AwLog.d(TAG, "sendHeartBeat", "...");
            characteristic.setValue(AppBleCmdUtil.getHeartCmd());
            mBluetoothGatt.writeCharacteristic(characteristic);
        } else {
            AwLog.d(TAG, "sendHeartBeat", "BluetoothGattService is null");
            return;
        }
        if(mHandler != null) {
            mHandler.postDelayed(mHeartRunnable, 1000);
        } else {
            AwLog.d(TAG, "sendHeartBeat", "Handler is null");
        }
    }

    public class HeartRunnable implements Runnable {

        @Override
        public void run() {
            sendHeartBeat();
        }
    }

    public void controlCarByBle(int cmdType) {
        if(!isConnected) {
            AwLog.d(TAG, "controlCarByBle", "!isConnected" + " ,cmdType: " + cmdType);
            CURRENT_CMD_TYPE = CMD_TYPE_DEFAULT;
            AwBleCallbackUtil.controlCallback(mBleControlListener, BleEnum.BLE_CONNECT_NONE, cmdType, CONTROL_STEP_FAIL, null);
            return;
        }
        if(!isAuth) {
            AwLog.d(TAG, "controlCarByBle", "!isAuth" + " ,cmdType: " + cmdType);
            CURRENT_CMD_TYPE = CMD_TYPE_DEFAULT;
            closeConnect(true);
            AwBleCallbackUtil.controlCallback(mBleControlListener, BleEnum.BLE_CONNECT_AUTH_FAIL, cmdType, CONTROL_STEP_FAIL, null);
            return;
        }
        AwLog.d(TAG, "controlCarByBle", "SecKey: " + AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(mContext)) + " ,cmdType: " + cmdType);
        BluetoothGattService service = getGattService(UUID_SERVICE);
        if (service != null) {
            BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID_CONTROL);
            switch (cmdType) {
                case CMD_TYPE_UNLOCK:
                    AwLog.d(TAG, "controlCarByBle","control unlock...");
                    CURRENT_CMD_TYPE = CMD_TYPE_UNLOCK;
                    characteristic.setValue(AppBleCmdUtil.getEncryptLockCmd(mContext, false));
                    break;
                case CMD_TYPE_LOCK:
                    AwLog.d(TAG, "controlCarByBle", "control lock...");
                    CURRENT_CMD_TYPE = CMD_TYPE_LOCK;
                    characteristic.setValue(AppBleCmdUtil.getEncryptLockCmd(mContext, true));
                    break;
                case CMD_TYPE_FIND_CAR:
                    AwLog.d(TAG, "controlCarByBle", "control find...");
                    CURRENT_CMD_TYPE = CMD_TYPE_FIND_CAR;
                    characteristic.setValue(AppBleCmdUtil.getEncryptFindCmd(mContext));
                    break;
            }
            mBluetoothGatt.writeCharacteristic(characteristic);
            AwLog.d(TAG, "controlCarByBle","write finish");
            if(mBleControlListener == null) {
                AwLog.d(TAG, "controlCarByBle", "ControlListener is null");
                return;
            }
            AwBleCallbackUtil.controlCallback(mBleControlListener, null, CURRENT_CMD_TYPE, CONTROL_STEP_CMD_SEND, null);
        } else {
            AwLog.d(TAG, "controlCarByBle","BluetoothGattService is null");
            closeConnect(true);
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
            AwBleCallbackUtil.controlCallback(mBleControlListener, BleEnum.BLE_CONTROL_FAIL, CURRENT_CMD_TYPE, CONTROL_STEP_FAIL, null);
        }
    }

    private void handleDiscoveryGattService(BluetoothGatt gatt) {
        if(isNotifySuccess) {
            AwLog.d(TAG, "handleDiscoveryGattService","isNotifySuccess return.");
            return;
        }
        if (gatt == null) {
            AwLog.d(TAG, "handleDiscoveryGattService","BluetoothGatt is null, return.");
            return;
        }
        List<BluetoothGattService> list = gatt.getServices();
        if(AwDataUtil.isEmpty(list)) {
            AwLog.d(TAG, "handleDiscoveryGattService", "gatt.getServices is null, return.");
            return;
        }
        for(BluetoothGattService service : list) {
            AwLog.d(TAG, "handleDiscoveryGattService", "serviceUUID: " + service.getUuid());
            if(service.getUuid().toString().contains("ff78")) {
                UUID_SERVICE = service.getUuid();
                List<BluetoothGattCharacteristic> subList = service.getCharacteristics();
                for(BluetoothGattCharacteristic subTemp : subList) {
                    AwLog.d(TAG, "handleDiscoveryGattService", "discovery uuid FF78 characteristic uuid..." + subTemp.getUuid());
                    if(subTemp.getUuid().toString().contains("7800")) {
                        UUID_AUTH = subTemp.getUuid();
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_AUTH..." + UUID_AUTH);
                    }
                    if(subTemp.getUuid().toString().contains("7801")) {
                        UUID_CONTROL = subTemp.getUuid();
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_CONTROL..." + UUID_CONTROL);
                    }
                    if(subTemp.getUuid().toString().contains("7802")) {
                        UUID_QUERY = subTemp.getUuid();
                        UUID_HEART = subTemp.getUuid();
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_QUERY..." + UUID_QUERY);
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_HEART..." + UUID_HEART);
                    }
                    if(subTemp.getUuid().toString().contains("7803")) {
                        UUID_REPORT = subTemp.getUuid();
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_REPORT..." + UUID_REPORT);
                    }
                    if(subTemp.getUuid().toString().contains("7804")) {
                        UUID_PAIR = subTemp.getUuid();
                        AwLog.d(TAG, "handleDiscoveryGattService", "UUID_PAIR..." + UUID_PAIR);
                    }
                }
                setNotify();
                AwLog.d(TAG, "handleDiscoveryGattService","Connect discovery uuid FF78...");
            }
            if(service.getUuid().toString().contains("2600")) {
                UUID_OTA = service.getUuid();
                AwLog.d(TAG, "handleDiscoveryGattService","Connect discovery uuid 2600...");
            }

        }
        mHandler.postDelayed(() -> {
            AwLog.d(TAG, "handleDiscoveryGattService","requestMtu: 128");
            gatt.requestMtu(128);
        }, 500);
    }

    public class MyOnCmdListener implements OnCmdListener {

        @Override
        public void onAuthSuccess() {
            isAuth = true;
            authCount = 0;
            startHeatBeatTask();
            AwLog.d(TAG, "OnCmdListener", "onAuthSuccess");
            if(mBleConnectListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onAuthSuccess ConnectListener is null");
                return;
            }
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_AUTH_SUCCESS, TAG_CONNECTED_AUTH);
            //发送手机型号信息
//            sendModelInfo();
        }

        @Override
        public void onAuthFail() {
            isAuth = false;
            AwLog.d(TAG, "OnCmdListener", "onAuthFail, authCount: " + authCount);
            if(authCount < 2) {
                authCount++;
                mHandler.postDelayed(new MyAuthRunnable(), 500);
                return;
            }
            authCount = 0;
            if(mBleConnectListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onAuthFail ConnectListener is null, return.");
                return;
            }
            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_AUTH_FAIL, TAG_CONNECTED);
            closeConnect(true);
        }

        @Override
        public void onControlStart(int type) {
            AwLog.d(TAG, "OnCmdListener", "onControlStart");
            if(mBleControlListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onControlStart ControlListener is null");
                return;
            }
            AwBleCallbackUtil.controlCallback(mBleControlListener, null, type, CONTROL_STEP_UNDER_CONTROL, null);
        }

        @Override
        public void onControlSuccess(int type) {
            AwLog.d(TAG, "OnCmdListener", "onControlSuccess");
            if(mBleControlListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onControlSuccess ControlListener is null");
                return;
            }
            AwBleCallbackUtil.controlCallback(mBleControlListener, BleEnum.BLE_COMMON_SUCCESS, type, CONTROL_STEP_SUCCESS, null);
            CURRENT_CMD_TYPE = CMD_TYPE_DEFAULT;
        }

        @Override
        public void onControlFail(int type, String msg) {
            AwLog.d(TAG, "OnCmdListener", "onControlFail");
            if(mBleControlListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onControlFail ControlListener is null");
                return;
            }
            AwBleCallbackUtil.controlCallback(mBleControlListener, BleEnum.BLE_CONTROL_FAIL, type, CONTROL_STEP_FAIL, msg);
            CURRENT_CMD_TYPE = CMD_TYPE_DEFAULT;
        }

        @Override
        public void onCarStatus(CarStatusBean bean) {
            AwLog.d(TAG, "OnCmdListener", "onCarStatus callback");
            if(mBleVehicleStatusListener == null) {
                AwLog.d(TAG, "OnCmdListener", "onCarStatus CarStatusListener is null");
                return;
            }
            mBleVehicleStatusListener.getCarStatusResult(bean);
        }
    }

    public class MyCountTime extends CountDownTimer {

        public MyCountTime(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onFinish() {
            if(isScanning) {
                stopScanBle();
                AwLog.d(TAG, "MyCountTime", "onFinish stopScan, The specified Bluetooth device was not found");
                AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NONE_SEARCH_DEVICE, TAG_CONNECTED_NONE);
            }
        }

        @Override
        public void onTick(long millisUntilFinished) {
        }
    }

    public class MyScanCallback extends ScanCallback {

        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            // Android5.0新增的扫描API，扫描返回的结果更友好，比如BLE广播数据以前是byte[] scanRecord，而新API帮我们解析成ScanRecord类
            if(TextUtils.isEmpty(result.getDevice().getName())) {
                AwLog.d(TAG, "ScanCallback", "onScanResult device name is null and address: " + result.getDevice().getAddress());
                return;
            }
            //ScanRecord中打印了, 这里不再打印deviceName
//            String deviceName = result.getDevice().getName();
//            BleLog.dN(TAG, "belservice ScanCallback name: " + deviceName);

            // result.getScanRecord() 获取BLE广播数据
            ScanRecord scanRecord = result.getScanRecord();
            try {
                AwLog.d(TAG, "ScanCallback","is exist scanRecord: " + AwBleUtil.printScanRecord(scanRecord));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                AwLog.d(TAG, "ScanCallback","printScanRecord Exception msg: " + e.getMessage());
            }
            SparseArray<byte[]> sparseArray = scanRecord.getManufacturerSpecificData();
            if(sparseArray == null || sparseArray.size() < 1) {
                AwLog.d(TAG, "ScanCallback","name is exist, but sparseArray is null");
//                cancelScanCountdown();
//                BleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
                return;
            }
            byte[] macByte = new byte[6];
            for(int i = 0; i < sparseArray.size(); i++) {
                int key = sparseArray.keyAt(i);
                Object obj = sparseArray.valueAt(i);
                if(obj instanceof byte[]) {
                    byte[] temp = (byte[]) obj;
                    if(temp.length > 6) {
                        macByte[0] = temp[1];
                        macByte[1] = temp[2];
                        macByte[2] = temp[3];
                        macByte[3] = temp[4];
                        macByte[4] = temp[5];
                        macByte[5] = temp[6];
                    }
                    AwLog.d(TAG, "ScanCallback","onScanResult sparseArray i: " + i + " ,key: " + key + " ,byte value: " + AwByteUtil.printBytes(temp));
                }
            }
            AwLog.d(TAG, "ScanCallback","onScanResult macByte : " + AwByteUtil.printBytes(macByte));

            String macStr = "";
            if(macByte == null || macByte.length == 0) {
                AwLog.d(TAG, "ScanCallback","name is exist, but macByte is null");
                return;
            }
            AwLog.d(TAG, "ScanCallback","6 origin: " + AwByteUtil.bytesToHexString(macByte));
            macStr = AwByteUtil.getMacString(macByte);
            AwLog.d(TAG, "ScanCallback","result MAC: " + macStr);
            String currentMac = AwAesUtil.deFile(AwBleSpUtil.getBleMac(mContext));
            AwLog.d(TAG, "ScanCallback","current MAC: " + currentMac);
            if(!AwDataUtil.isEmpty(macStr) && macStr.equals(currentMac)) {
                mBluetoothDevice = result.getDevice();
                AwLog.d(TAG, "ScanCallback","check pass, to connect device info: "  + mBluetoothDevice.toString());
                toConnect();
            }
        }
    }

    private void toConnect() {
        if(mBluetoothDevice == null) {
            AwLog.d(TAG, "toConnect","BluetoothDevice is null...return.");
            return;
        }
        AwLog.d(TAG, "toConnect", "device info: " + mBluetoothDevice.getName() + " ,address: " + mBluetoothDevice.getAddress());
        cancelScanCountdown();
        connectBle(mBluetoothDevice);
    }

    public class MyAuthRunnable implements Runnable {

        @Override
        public void run() {
            authentication();
        }
    }

    public void reConnectBle() {
        if(isDisConnecting) {
            AwLog.d(TAG, "reConnectBle", "isDisConnecting, so delay reConnect...");
            mHandler.postDelayed(new ReConnectRunnable(), 3000);
            return;
        }
        AwLog.d(TAG, "reConnectBle", "...");
        mHandler.postDelayed(new ReConnectRunnable2(), 2000);
    }

    public class ReConnectRunnable implements Runnable {

        @Override
        public void run() {
            reConnectBle();
        }
    }

    public class ReConnectRunnable2 implements Runnable {

        @Override
        public void run() {
            isRetry = true;
            connectBle(mBluetoothDevice);
        }
    }

    public class MyBluetoothGattCallback extends BluetoothGattCallback {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//            callbackCurrentBleDevice(gatt);
            mBluetoothDevice = gatt.getDevice();
            AwLog.d(TAG, "GattCallback", String.format("onConnectionStateChange: %s,%s,%s,%s", mBluetoothDevice.getName(), mBluetoothDevice.getAddress(), status, newState));
            if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
                isConnected = true;
                isDiscovery = gatt.discoverServices(); //启动服务发现
                AwLog.d(TAG, "GattCallback","onConnectionStateChange, discoverServices: " + isDiscovery);
                boolean rssiStatus = mBluetoothGatt.readRemoteRssi();
                AwLog.d(TAG, "GattCallback","onConnectionStateChange, readRemoteRssi: " + rssiStatus);
            } else {
                AwLog.d(TAG, "GattCallback","onConnectionStateChange, close connect");
                isConnected = false;
                closeConnect(false);
                cancelHeatBeatTask();
            }
            if(mBleConnectListener != null) {
                if(status == 0) {
                    if(newState == 2) {
                        AwLog.d(TAG, "GattCallback","onConnectionStateChange newState=2, " + String.format("belservice [%s] connected", mBluetoothDevice));
                        AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_SUCCESS, isAuth ? TAG_CONNECTED_AUTH : TAG_CONNECTED);
                    } else {
                        AwLog.d(TAG, "GattCallback","onConnectionStateChange newState!=2, " + String.format("belservice [%s] disConnected", mBluetoothDevice));
                        AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_FAIL, TAG_CONNECTED_NONE);
                    }
                } else {
                    AwLog.d(TAG, "GattCallback","onConnectionStateChange status!=0, " + String.format("belservice [%s] connect error", status));
                    if(status == 8) {
                        AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_FAIL_DISTANCE, TAG_CONNECTED_NONE);
                    } else if(status == 19) {
                        AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_FAIL_LATER_RETRY, TAG_CONNECTED_NONE);
                    } else {
                        if(!isRetry){
                            AwLog.d(TAG, "GattCallback","onConnectionStateChange status !=0, retry connect dalay");
                            reConnectBle();
                            return;
                        }
                        AwLog.d(TAG, "GattCallback","onConnectionStateChange connect fail, status is " + status + " and retry fail.");
                        if(status == 133) {
                            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_FAIL_FREQUENTLY, TAG_CONNECTED_NONE);
                        } else {
                            AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_FAIL, TAG_CONNECTED_NONE);
                        }
                    }
                }
            } else {
                AwLog.d(TAG, "GattCallback","onConnectionStateChange ConnectCallback is null");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            AwLog.d(TAG, "GattCallback", String.format("onServicesDiscovered: %s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), status));

            //BLE服务发现不成功
            if(status != BluetoothGatt.GATT_SUCCESS) {
                AwLog.d(TAG, "GattCallback","服务发现失败...断开连接");
                closeConnect(false);
                AwBleCallbackUtil.connectCallback(mBleConnectListener, BleEnum.BLE_CONNECT_NO_SERVICE, TAG_CONNECTED_NONE);
                return;
            }
            handleDiscoveryGattService(gatt);
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            AwLog.d(TAG, "GattCallback","onMtuChanged: " + mtu);
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            UUID uuid = characteristic.getUuid();
            String valueStr = AwByteUtil.printBytes(characteristic.getValue());
            AwLog.d(TAG, "GattCallback", String.format("onCharacteristicRead: %s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
            if(mBleSpareListener != null) {
                mBleSpareListener.onCharacteristicRead("belservice read Characteristic[" + uuid + "]:\n" + valueStr);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
//            callbackCurrentBleDevice(gatt);
            UUID uuid = characteristic.getUuid();
            String valueStr = AwByteUtil.printBytes(characteristic.getValue());

            AwLog.d(TAG, "GattCallback", String.format("onCharacteristicWrite: %s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
            if(mBleSpareListener != null) {
                mBleSpareListener.onCharacteristicWrite("belservice write Characteristic[" + uuid + "]:\n" + valueStr);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            //下面2个方法都可正常打印
//            BleLog.dN(TAG, "onCharacteristicChanged value printByte: " + BleByteUtil.printByte(characteristic.getValue()));
//            BleLog.dN(TAG, "onCharacteristicChanged value: " + BleByteUtil.bytesToHexString(characteristic.getValue()));
            UUID uuid = characteristic.getUuid();
            String valueStr = AwByteUtil.printBytes(characteristic.getValue());
            AwLog.d(TAG, "GattCallback", String.format("onCharacteristicChanged: %s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr));
            if(mBleSpareListener != null) {
                mBleSpareListener.onCharacteristicChanged("belservice notify Characteristic[" + uuid + "]:\n" + valueStr);
            }
            if(isAuthConnected()) {
                AwLog.d(TAG, "GattCallback", "onCharacteristicChanged isAuthConnected, filter 7804");
                AppBleCmdUtil.judgeBleCmdResult(mContext, CURRENT_CMD_TYPE, characteristic, new MyOnCmdListener());
                return;
            }
            if(uuid.toString().contains("7804")) {
                AwLog.d(TAG, "GattCallback", "onCharacteristicChanged uuid 7804...");
                byte[] callbackValueByte = characteristic.getValue();
                //STEP1 接收配对成功通知0X80指令, 发送用户index位
                if(AwByteUtil.printBytes(callbackValueByte).contains("80")) {
                    AwLog.d(TAG, "GattCallback", "STEP1 接收配对成功通知0X80指令, 发送用户index位");
                    byte[] cmdByte = new byte[2];
                    cmdByte[0] = 0x00;
                    String keyIndex = AwAesUtil.deFile(AwBleSpUtil.getBleMacIndex(mContext));
                    AwLog.d(TAG, "GattCallback", "配对keyIndex: " + keyIndex);
                    cmdByte[1] = AwByteUtil.hexStringToBytes(AwAesUtil.deFile(AwBleSpUtil.getBleMacIndex(mContext)))[0];
                    AwLog.d(TAG, "GattCallback", "配对cmdByte: " + AwByteUtil.printBytes(cmdByte));
                    characteristic.setValue(cmdByte);
                    mBluetoothGatt.writeCharacteristic(characteristic);
                } else if(AwByteUtil.printBytes(callbackValueByte).contains("81")) {
                    //STEP2 接收到index回复通知 0x81, 命令0x00
                    if(callbackValueByte.length > 1 && callbackValueByte[1] == 0x00) {
                        gatt.discoverServices(); //启动服务发现
                        AwLog.d(TAG, "GattCallback", "STEP2 接收到index回复通知 0x81, 命令0x00");
                        //STEP3 发起鉴权
                        mHandler.postDelayed(new MyAuthRunnable(), 500);
                    }
                }
            } else {
                AwLog.d(TAG, "GattCallback", "onCharacteristicChanged uuid other...judgeBleCmdResult");
                AppBleCmdUtil.judgeBleCmdResult(mContext, CURRENT_CMD_TYPE, characteristic, new MyOnCmdListener());
            }
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//            callbackCurrentBleDevice(gatt);
            UUID uuid = descriptor.getUuid();
            String valueStr = Arrays.toString(descriptor.getValue());
            AwLog.d(TAG, "GattCallback", String.format("onDescriptorRead: %s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
            if(mBleSpareListener != null) {
                mBleSpareListener.onDescriptorRead("belservice read Descriptor[" + uuid + "]:\n" + valueStr);
            }
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
//            callbackCurrentBleDevice(gatt);
            UUID uuid = descriptor.getUuid();
            String valueStr = Arrays.toString(descriptor.getValue());

            AwLog.d(TAG, "GattCallback", String.format("onDescriptorWrite: %s,%s,%s,%s,%s", gatt.getDevice().getName(), gatt.getDevice().getAddress(), uuid, valueStr, status));
            if(mBleSpareListener != null) {
                mBleSpareListener.onDescriptorWrite("belservice write Descriptor[" + uuid + "]:\n" + valueStr);
            }
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            AwLog.d(TAG, "GattCallback","onReadRemoteRssi rssi: " + rssi);
            if(mBleSpareListener != null && status == BluetoothGatt.GATT_SUCCESS) {
                mBleSpareListener.getRssi(rssi, AwBleRssiUtil.getDistance(rssi));
            }
        }
    }

}
